package com.github.codemperor.core.retry;


import com.github.codemperor.core.logger.BaseLogger;
import com.github.codemperor.core.logger.DefaultLogger;
import com.github.codemperor.core.retry.bean.RetryResult;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.function.Consumer;

/**
 * 同步重试框架
 */
public class RetryOnException {
    private BaseLogger log = DefaultLogger.logger(this.getClass());

    private RetryOnException(Integer retryTimes, Boolean randomDelay, Integer randomDelaySecond, Integer delayTime) {
        if (null != retryTimes) {
            this.retryTimes = retryTimes < 0 ? this.retryTimes : retryTimes;
        }
        if (null != randomDelay) {
            this.randomDelay = randomDelay;
        }
        if (null != randomDelaySecond) {
            this.randomDelaySecond = randomDelaySecond < 0 ? this.randomDelaySecond : randomDelaySecond;
        }
        if (null != delayTime) {
            this.delayTime = delayTime < 0 ? this.delayTime : delayTime;
        }
    }

    /**
     * @param retryTimes 重试次数，默认1次
     * @param delayTime  重试时间，默认0s
     * @return
     */
    public static RetryOnException builder(Integer retryTimes, Integer delayTime) {
        return new RetryOnException(retryTimes, null, null, delayTime);
    }

    /**
     * @param retryTimes        重试次数，默认1次
     * @param randomDelay       是否开启随机重试
     * @param randomDelaySecond 随机重试的随机最大延时秒数
     * @return
     */
    public static RetryOnException builder(Integer retryTimes, Boolean randomDelay, Integer randomDelaySecond) {
        return new RetryOnException(retryTimes, randomDelay, randomDelaySecond, null);
    }

    /**
     * 使用默认的配置，重试1次。每次重试间隔0s
     *
     * @return
     */
    public static RetryOnException builder() {
        return new RetryOnException(null, null, null, null);
    }

    /**
     * 重试结果
     */
    private List<RetryResult> results = new ArrayList<>();

    /**
     * 重试次数
     */
    private int retryTimes = 1;

    /**
     * 重试延迟时间，单位秒， 默认0延时
     */
    private int delayTime = 0;

    /**
     * 开启随机2延迟 0-10s
     */
    private boolean randomDelay = false;

    /**
     * 随机延时最大秒，最小值默认为0
     */
    private int randomDelaySecond = 5;

    /**
     * 终止重试的阈值
     */
    private boolean stop = false;

    /**
     * 内部重试处理
     * 通过异常进行判定业务是否执行成功
     * 如果业务中自己catch异常，当如果需要重试的时候请抛出异常，如果需要停止，请手动停止重试
     *
     * @param function   业务回调
     * @param isAutoStop 是否业务成功后自动终止重试
     */
    private void retryProcess(Consumer<RetryOnException> function, boolean isAutoStop) {
        long sleepTime = delayTime;
        for (int i = 0; i <= retryTimes; i++) {
            long startMillis = System.currentTimeMillis();

            //终止重试
            if (stop) {
                break;
            }

            // 初始化重试时间
            RetryResult result = new RetryResult();
            if (randomDelay) {
                sleepTime = new Random().nextInt(randomDelaySecond) + 1; //这里随机可能出现0，所以+1
            }

            try {
                result.setMillisecondDelay(0L);
                if (i > 0) { //第一次直接执行，无需等待
                    Thread.sleep(sleepTime * 1000);
                    result.setMillisecondDelay(sleepTime * 1000);
                }

                function.accept(this);
                result.setSuccess(true);

                if (isAutoStop) {
                    stop();//自动结束
                }

            } catch (Exception e) { //报错后立即重试
                log.error("retry times: " + (i + 1), e);
                result.setEx(e);
                result.setSuccess(false);
            }

            // 获取重试结果
            result.setMillisecond(System.currentTimeMillis() - startMillis);
            result.setNumber(i + 1);
            results.add(result);
        }
    }

    /**
     * 执行重试
     *
     * @param function 业务实现
     * @return
     */
    public RetryOnException execute(Consumer<RetryOnException> function) {
        retryProcess(function, false);
        return this;
    }

    /**
     * 执行重试并且业务成功后自动终止重试
     * ！！！通常判定业务是否失败，依靠报错异常，如果不报错，认为不会有任何问题！！！
     *
     * @param function 业务实现
     * @return
     */
    public RetryOnException executeAndStopIfSuccess(Consumer<RetryOnException> function) {
        retryProcess(function, true);
        return this;
    }

    /**
     * 获取重试结果
     *
     * @return
     */
    public List<RetryResult> getResults() {
        return results;
    }

    /**
     * 终止重试 回调, 推荐调用者自己终止
     * 或者调用executeAndStopIfSuccess方法自动终止
     */
    public void stop() {
        this.stop = true;
    }
}
